package tt.dz.order.util.refund;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.Comparator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.dom4j.DocumentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import tt.dz.order.dao.SysApplicationContextAware;
import tt.dz.order.exception.BusinessException;
import tt.dz.order.pojo.AliPayCallBackRecord;
import tt.dz.order.util.AliPayUtil;
import tt.dz.order.util.OrderConstant.RuntimeMode;
import tt.dz.order.util.UUIDUtil;
import tt.dz.order.util.XmlUtil;

/**
 * 
 * 
 * @name 支付宝退款
 * @author Anson
 * @version 1.0
 * @summary 依托与sprign管理，生命周期为prototype<br>
 *          退款方式位
 * @desc 当前bean的依托与spring管理，业务获取依赖与
 *       {@linkplain RefundingReuestFactory#generate(tt.dz.order.util.OrderConstant.OrderPayWay, tt.dz.order.util.OrderConstant.OrderPayChannelCode)}
 * 
 *
 */
@Component(value = RefundingReuestFactory.SPRING_PRE_IDENTIIED + "_1_ALI")
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class RefundingRequestToAliPay extends AbstractRefundingRequest {

	
	private static final transient Logger LOGGER = LoggerFactory.getLogger(RefundingRequestToAliPay.class);
	
	// 阿里支付的退款服务名称
	private static final String ALI_REFUND_SERVICE = "refund_fastpay_by_platform_nopwd";

	private static final String ALI_REFUND_DETAIL_DATA_FMT = "%s^%s^%s";

	private static Comparator<String> COMPARATOR = new AlipayComparator();

	@Override
	public boolean refund() throws BusinessException {
		this.validate();
		// ali支付工具
		AliPayUtil aliPayUtil = SysApplicationContextAware.getApplication().getBean("aliPayUtil", AliPayUtil.class);
		// 寻找发生支付的记录的订单下支付宝支付记录
		AliPayCallBackRecord aliPayCallBackRecord = this.aliPayCallBackRecord(this.payRecordRelateOrder.getOrderNo());
		if (aliPayCallBackRecord == null) {
			throw new BusinessException("当前订单并未找到支付宝付款记录");
		}
		// 要发送的参数
		Map<String, String> paramMap = new AliPayParamMap();
		// 待退款业务的金额
		String refundFeeForSend = this.refundOrder.getCaculatedTotalPrice().abs().toString();
		// 退款请求时间
		String refundDate = this.generaterefundDate();
		// 退款批次号
		String batchNo = this.generateAliBatchNo();
		// 总笔数-->即参数detail_data的值中，“#”字符出现的数量加1，最大支持1000笔（即“#”字符出现的最大数量为999个）。
		String batchNum = "1";
		// 单笔数据集
		String detailData = this.generatedetailData(aliPayCallBackRecord.getTradeNo(), refundFeeForSend, this.reason);
		// 签名
		String sign = null;
		paramMap.put("service", ALI_REFUND_SERVICE);
		paramMap.put("partner", aliPayUtil.PARTNER);
		paramMap.put("_input_charset", AliPayUtil.INPUT_CHARSET);
		paramMap.put("sign_type", AliPayUtil.SIGN_TYPE);
		paramMap.put("seller_email", aliPayUtil.SELLER_EMAIL);
		paramMap.put("seller_user_id", aliPayUtil.SELLER_USER_ID);
		paramMap.put("refund_date", refundDate);
		paramMap.put("batch_no", batchNo);
		paramMap.put("batch_num", batchNum);
		paramMap.put("detail_data", detailData);
		try {
			sign = AliPayUtil.sign(paramMap);
		} catch (UnsupportedEncodingException e) {
			throw new BusinessException("当前支付宝退款业务，加密参数失败" + e.getMessage());
		}
		paramMap.put("sign", sign);
		LOGGER.info("支付宝退款的发送参数:"+paramMap);
		if (RuntimeMode.DEBUG == RuntimeMode.curMode()) {
			//若是调试环境，则跳过真实发送退款业务到ali支付宝
			return true;
		}
		// 发送退款业务
		String retStr = AliPayUtil.sendRefundRequest(paramMap);
		if (retStr == null || retStr.trim().length() == 0) {
			throw new BusinessException("当前支付宝退款业务，调用ali接口执行失败");
		}
		Map<String, Object> retMap = null;
		try {
			retMap = XmlUtil.Xml2Map(retStr);
		} catch (DocumentException e) {
			throw new BusinessException("当前支付宝退款业务，解析返回参数失败" + e.getMessage());
		}
		String retResult = (String) retMap.get("is_success");
		return retResult != null && retResult.equals("T");
	}

	/**
	 * 单笔数据集<br>
	 * 退款请求的明细数据。格式详情参见下面的“单笔数据集参数说明”。<br>
	 * 单笔数据集参数说明<br>
	 * 单笔数据集格式为：第一笔交易退款数据集#第二笔交易退款数据集#第三笔交易退款数据集…#第N笔交易退款数据集；<br>
	 * 交易退款数据集的格式为：原付款支付宝交易号^退款总金额^退款理由；<br>
	 * 不支持退分润功能。<br>
	 * 单笔数据集（detail_data）注意事项<br>
	 * detail_data中的退款笔数总和要等于参数batch_num的值；<br>
	 * “退款理由”长度不能大于256字节，“退款理由”中不能有“^”、“|”、“$”、“#”等影响detail_data格式的特殊字符；<br>
	 * detail_data中退款总金额不能大于交易总金额；<br>
	 * 一笔交易可以多次退款，退款次数最多不能超过99次，需要遵守多次退款的总金额不超过该笔交易付款金额的原则。<br>
	 * 
	 * @param aliPayTradeNo
	 *            原付款支付宝交易号
	 * @param refundMoney
	 *            退款总金额
	 * @param reason
	 *            退款理由
	 * @throws BusinessException
	 * @return
	 */
	private String generatedetailData(String aliPayTradeNo, String refundMoney, String reason)
			throws BusinessException {

		if (StringUtils.isEmpty(aliPayTradeNo) || StringUtils.isEmpty(refundMoney)
				|| StringUtils.isEmpty(refundMoney)) {
			throw new BusinessException("当前支付宝退款业务，封装详细参数时候，缺少参数");
		}
		if (!NumberUtils.isNumber(refundMoney)) {
			throw new BusinessException("当前支付宝退款业务，封装详细参数时候，退款金额非数字");
		}
		return String.format(ALI_REFUND_DETAIL_DATA_FMT, aliPayTradeNo, refundMoney, reason);
	}

	/**
	 * 退款的时间电
	 * 
	 * @return
	 */
	private String generaterefundDate() {
		return UUIDUtil.getCurrentDate(null, "yyyy-MM-dd hh:mm:ss");
	}

	/**
	 * 退款的批次号<br>
	 * 用退款单号作为批次号里的流水号部分
	 * 
	 * @return
	 */
	private String generateAliBatchNo() {
		return new StringBuilder(UUIDUtil.getCurrentDate(null, "yyyyMMdd")).append(refundOrder.getOrderNo()).toString();
	}

	/**
	 * 校验当前订单信息
	 * 
	 * @param orderInfo
	 * @throws BusinessException
	 */
	private void validate() throws BusinessException {

		if (this.refundOrder == null) {
			throw new BusinessException("支付宝退款业务中缺少订单信息");
		}
		if (this.payRecordRelateOrder == null) {
			throw new BusinessException("支付宝退款业务中缺少发生支付的订单信息");
		}
		if (StringUtils.isEmpty(this.refundOrder.getOrderNo())) {
			throw new BusinessException("支付宝退款业务中缺少订单号");
		}
		// 当前订单金额为负值的时候才允许退款
		boolean isNeedRefund = new BigDecimal(0).compareTo(this.refundOrder.getCaculatedTotalPrice()) > 0;
		if (!isNeedRefund) {
			throw new BusinessException("支付宝退款业务中订单金额是不满足退款的业务");
		}
	}

	/**
	 * 
	 * 
	 * @name ali退款的参数Map
	 * @author Anson
	 * @version 1.0
	 * @summary
	 * @desc
	 *
	 */
	static class AliPayParamMap extends TreeMap<String, String> {

		/**
		 * 
		 */
		private static final long serialVersionUID = -3932791157740100853L;

		public AliPayParamMap() {
			super(COMPARATOR);
		}

		public AliPayParamMap(Comparator<? super String> comparator) {
			super(comparator);
		}

		public AliPayParamMap(Map<? extends String, ? extends String> m) {
			super(COMPARATOR);
			putAll(m);
		}

		public AliPayParamMap(SortedMap<String, ? extends String> m) {
			super(COMPARATOR);
			putAll(m);
		}
	}

	/**
	 * 
	 * 
	 * @name ali支付的key比较器
	 * @author Anson
	 * @version 1.0
	 * @summary
	 * @desc
	 *
	 */
	private static class AlipayComparator implements Comparator<String> {
		@Override
		public int compare(String key1, String key2) {
			return key1.compareTo(key2);
		}
	}

}
